import org.apache.commons.lang3.StringUtils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CharacterHelper {

    private static final char SBC_CHAR_START = 65281; // 全角 ！
    private static final char SBC_CHAR_END = 65374; // 全角 ~
    private static final int CONVERT_STEP = 65248; // 全角半角转换间隔
    private static final char SBC_SPACE = 160; // 全角空格
    private static final char SBC_SPACE3 = 8194; // 全角空格
    private static final char SBC_SPACE4 = 8201; // 全角空格
    private static final char SBC_SPACE2 = 12288; // 全角空格
    private static final char DBC_SPACE = 32; // 半角空格
    private static final char SBC_LINE = 8211; // 全角–
    private static final char DBC_LINE = 45; // 半角-
    private static final char SBC_LEFT_QUOTES = 8220;//”
    private static final char SBC_RIGHT_QUOTES = 8221;//”
    private static final char DBC_QUOTES = 34;
    private static final char[] MATH_MARKS = "=+×÷＞＜≥≤≮≯≡≠≈≒±√³√π℅∵∴∷㏒㏑∫∬∭∮∯∰∂∑∏∈∉∅⊂⊃⊆⊇⊄⊅⊊⊈⫋⫌∀∃∩∪∧∨⊙⊕∥⊥⌒∟∠△⊿∝∽∞≌°℃→∀".toCharArray();

    private static final char[] SENTENCE_SEPARATOR = "。.?？；;！!".toCharArray();
    private static final char[] KEYWORD_SEPARATOR = ",，；; ".toCharArray();

    /**
     * 全角转半角
     *
     * @param _char
     * @return
     */
    public static char fullToHalf(char _char) {
        if (_char >= SBC_CHAR_START && _char <= SBC_CHAR_END) { // 如果位于全角！到全角～区间内
            return (char) (_char - CONVERT_STEP);
        } else if (_char == SBC_SPACE || _char == SBC_SPACE2 || _char == SBC_SPACE3 || _char == SBC_SPACE4) { // 如果是全角空格
            return DBC_SPACE;
        } else if (_char == SBC_LINE) {
            return DBC_LINE;
        } else if (_char == SBC_LEFT_QUOTES || _char == SBC_RIGHT_QUOTES) {
            return DBC_QUOTES;
        } else { // 不处理全角空格，全角！到全角～区间外的字符
            return _char;
        }
        /**
         *  else if(_char=='。'){
         return '.';
         }
         */
    }

    public static String fullToHalf(String text) {
        if (text == null) {
            return null;
        }
        StringBuilder str = new StringBuilder();
        for (char c : text.toCharArray()) {
            str.append(fullToHalf(c));
        }
        return str.toString();
    }

    public static boolean containsHanScript(String s) {
        return s.codePoints().anyMatch(
                codepoint ->
                        Character.UnicodeScript.of(codepoint) == Character.UnicodeScript.HAN);
    }

    public static boolean containsNormalChineseChar(String s) {
        if (s != null) {
            for (char c : s.toCharArray()) {
                if (isNormalChineseChar(c)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取指定字符串出现的次数
     *
     * @param srcText  源字符串
     * @param findText 要查找的字符串
     * @return 出现的次数
     */
    public static int appearNumber(String srcText, String findText) {
        int count = 0;
        Pattern p = Pattern.compile(findText);
        Matcher m = p.matcher(srcText);
        while (m.find()) {
            count++;
        }
        return count;
    }

    /**
     * 判断一句话的分隔符
     *
     * @param s 关键词这句话
     * @return c 分隔符
     */
    public static String getSepChar(String s, int num) {
        if (s == null) {
            return null;
        }
        for (char c : KEYWORD_SEPARATOR) {
            // 论文关键词至少要有三个才算匹配上
            if (appearNumber(s, String.valueOf(c)) > num) {
                return String.valueOf(c);
            }
        }
        return null;
    }


    public static boolean containsEnglishChar(String s) {
        if (s != null) {
            for (char c : s.toCharArray()) {
                if (isEnglishChar(c)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static boolean everyCharIsEnglish(String s) {
        if (s != null) {
            for (char c : s.toCharArray()) {
                if (!isEnglishChar(c)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    // 常用汉字字符，不在这范围内的汉字，一般不用
    public static boolean isNormalChineseChar(char c) {
        return 0x4E00 <= c && c <= 0x9FA5;
    }

    public static boolean isChineseChar(char c) {
        return containsHanScript(c + "");
    }

    public static boolean isEnglishChar(char c) {
        return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z';
    }

    public static boolean isEnglishOrChineseChar(char c) {
        return isEnglishChar(c) || isNormalChineseChar(c);
    }

    public static boolean isNumberChar(char c) {
        return '0' <= c && c <= '9';
    }

    public static boolean isMathChar(char c) {
        for (char mathMark : MATH_MARKS) {
            if (c == mathMark) {
                return true;
            }
        }
        return false;
    }

    // 替换所有标点符号和空格
    public static String replaceAllMarks(String str, String replacement) {
        if (str == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (char c : str.toCharArray()) {
            if (CharacterHelper.isMark(c)) {
                sb.append(replacement);
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    public static boolean isMark(char ch) {
        return isSymbol(ch) || isPunctuation(ch) || isOtherPunc(ch) || isWhiteSpace(ch);
    }

    /**
     * 转义正则特殊字符 （$()*+.[]?\^{},|）
     */
    public static String escapeExprSpecialWord(String keyword) {
        if (StringUtils.isNotBlank(keyword)) {
            String[] fbsArr = {"\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|"};
            for (String key : fbsArr) {
                if (keyword.contains(key)) {
                    keyword = keyword.replace(key, "\\" + key);
                }
            }
        }
        return keyword;
    }

    private static boolean isSymbol(char ch) {
        if (isCnSymbol(ch)) return true;
        if (isEnSymbol(ch)) return true;

        if (0x2010 <= ch && ch <= 0x2017) return true;
        if (0x2020 <= ch && ch <= 0x2027) return true;
        if (0x2B00 <= ch && ch <= 0x2BFF) return true;
        if (0xFF03 <= ch && ch <= 0xFF06) return true;
        if (0xFF08 <= ch && ch <= 0xFF0B) return true;
        if (ch == 0xFF0D || ch == 0xFF0F) return true;
        if (0xFF1C <= ch && ch <= 0xFF1E) return true;
        if (ch == 0xFF20 || ch == 0xFF65) return true;
        if (0xFF3B <= ch && ch <= 0xFF40) return true;
        if (0xFF5B <= ch && ch <= 0xFF60) return true;
        if (ch == 0xFF62 || ch == 0xFF63) return true;
        if (ch == 0x0020 || ch == 0x3000) return true;
        return false;

    }

    private static boolean isCnSymbol(char ch) {
        if (0x3004 <= ch && ch <= 0x301C) return true;
        if (0x3020 <= ch && ch <= 0x303F) return true;
        return false;
    }

    private static boolean isEnSymbol(char ch) {

        if (ch == 0x40) return true;
        if (ch == 0x2D || ch == 0x2F) return true;
        if (0x23 <= ch && ch <= 0x26) return true;
        if (0x28 <= ch && ch <= 0x2B) return true;
        if (0x3C <= ch && ch <= 0x3E) return true;
        if (0x5B <= ch && ch <= 0x60) return true;
        if (0x7B <= ch && ch <= 0x7E) return true;

        return false;
    }

    private static boolean isPunctuation(char ch) {
        if (isCjkPunc(ch)) return true;
        if (isEnPunc(ch)) return true;

        if (0x2018 <= ch && ch <= 0x201F) return true;
        if (ch == 0xFF01 || ch == 0xFF02) return true;
        if (ch == 0xFF07 || ch == 0xFF0C) return true;
        if (ch == 0xFF1A || ch == 0xFF1B) return true;
        if (ch == 0xFF1F || ch == 0xFF61) return true;
        if (ch == 0xFF0E) return true;
        if (ch == 0xFF65) return true;

        return false;
    }

    private static boolean isEnPunc(char ch) {
        if (0x21 <= ch && ch <= 0x22) return true;
        if (ch == 0x27 || ch == 0x2C) return true;
        if (ch == 0x2E || ch == 0x3A) return true;
        if (ch == 0x3B || ch == 0x3F) return true;

        return false;
    }

    private static boolean isCjkPunc(char ch) {
        if (0x3001 <= ch && ch <= 0x3003) return true;
        if (0x301D <= ch && ch <= 0x301F) return true;

        return false;
    }

    public static boolean isWhiteSpace(char c) {
        if (c == '\f') return true;
        if (c == '\n') return true;
        if (c == '\r') return true;
        if (c == '\t') return true;
        if (c == '\b') return true;
        if (c == '\u00a0') return true;
        if (c == '\u1680') return true;
        if ('\u2000' <= c && c <= '\u200a') return true;
        if (c == '\u2028') return true;
        if (c == '\u2029') return true;
        if (c == '\u202f') return true;
        if (c == '\u205f') return true;
        if (c == '\u3000') return true;
        if (c == '\ufeff') return true;
        if (c == 11) return true;
        if (c == 32) return true;
        return false;
    }

    //通过运行
    // .replaceAll("\\pP", "")
    // .replaceAll("[+~$`^=|<>～｀＄＾＋＝｜＜＞￥×]", "")
    // .replaceAll("\\s+", "")
    // 对65535个char遍历，求isMark的补集而得，是对原isMark的补充
    private static boolean isOtherPunc(char c) {
        if (c < 5000) {
            if (c == 161) return true;
            if (c == 167) return true;
            if (c == 171) return true;
            if (182 <= c && c <= 183) return true;
            if (c == 187) return true;
            if (c == 191) return true;
            if (c == 215) return true;
            if (c == 894) return true;
            if (c == 903) return true;
            if (1370 <= c && c <= 1375) return true;
            if (1417 <= c && c <= 1418) return true;
            if (c == 1470) return true;
            if (c == 1472) return true;
            if (c == 1475) return true;
            if (c == 1478) return true;
            if (1523 <= c && c <= 1524) return true;
            if (1545 <= c && c <= 1546) return true;
            if (1548 <= c && c <= 1549) return true;
            if (c == 1563) return true;
            if (1566 <= c && c <= 1567) return true;
            if (1642 <= c && c <= 1645) return true;
            if (c == 1748) return true;
            if (1792 <= c && c <= 1805) return true;
            if (2039 <= c && c <= 2041) return true;
            if (2096 <= c && c <= 2110) return true;
            if (c == 2142) return true;
            if (2404 <= c && c <= 2405) return true;
            if (c == 2416) return true;
            if (c == 2800) return true;
            if (c == 3572) return true;
            if (c == 3663) return true;
            if (3674 <= c && c <= 3675) return true;
            if (3844 <= c && c <= 3858) return true;
            if (c == 3860) return true;
            if (3898 <= c && c <= 3901) return true;
            if (c == 3973) return true;
            if (4048 <= c && c <= 4052) return true;
            if (4057 <= c && c <= 4058) return true;
            if (4170 <= c && c <= 4175) return true;
            if (c == 4347) return true;
            if (4960 <= c && c <= 4968) return true;
        } else if (c < 20000) {
            if (c == 5120) return true;
            if (5741 <= c && c <= 5742) return true;
            if (5787 <= c && c <= 5788) return true;
            if (5867 <= c && c <= 5869) return true;
            if (5941 <= c && c <= 5942) return true;
            if (6100 <= c && c <= 6102) return true;
            if (6104 <= c && c <= 6106) return true;
            if (6144 <= c && c <= 6154) return true;
            if (6468 <= c && c <= 6469) return true;
            if (6686 <= c && c <= 6687) return true;
            if (6816 <= c && c <= 6822) return true;
            if (6824 <= c && c <= 6829) return true;
            if (7002 <= c && c <= 7008) return true;
            if (7164 <= c && c <= 7167) return true;
            if (7227 <= c && c <= 7231) return true;
            if (7294 <= c && c <= 7295) return true;
            if (7360 <= c && c <= 7367) return true;
            if (c == 7379) return true;
            if (8240 <= c && c <= 8259) return true;
            if (8261 <= c && c <= 8273) return true;
            if (8275 <= c && c <= 8286) return true;
            if (8317 <= c && c <= 8318) return true;
            if (8333 <= c && c <= 8334) return true;
            if (9001 <= c && c <= 9002) return true;
            if (10088 <= c && c <= 10101) return true;
            if (10181 <= c && c <= 10182) return true;
            if (10214 <= c && c <= 10223) return true;
            if (10627 <= c && c <= 10648) return true;
            if (10712 <= c && c <= 10715) return true;
            if (10748 <= c && c <= 10749) return true;
            if (11513 <= c && c <= 11516) return true;
            if (11518 <= c && c <= 11519) return true;
            if (c == 11632) return true;
            if (11776 <= c && c <= 11822) return true;
            if (11824 <= c && c <= 11835) return true;
            if (c == 12448) return true;
            if (c == 12539) return true;
        } else {
            if (42238 <= c && c <= 42239) return true;
            if (42509 <= c && c <= 42511) return true;
            if (c == 42611) return true;
            if (c == 42622) return true;
            if (42738 <= c && c <= 42743) return true;
            if (43124 <= c && c <= 43127) return true;
            if (43214 <= c && c <= 43215) return true;
            if (43256 <= c && c <= 43258) return true;
            if (43310 <= c && c <= 43311) return true;
            if (c == 43359) return true;
            if (43457 <= c && c <= 43469) return true;
            if (43486 <= c && c <= 43487) return true;
            if (43612 <= c && c <= 43615) return true;
            if (43742 <= c && c <= 43743) return true;
            if (43760 <= c && c <= 43761) return true;
            if (c == 44011) return true;
            if (64830 <= c && c <= 64831) return true;
            if (65040 <= c && c <= 65049) return true;
            if (65072 <= c && c <= 65106) return true;
            if (65108 <= c && c <= 65121) return true;
            if (c == 65123) return true;
            if (c == 65128) return true;
            if (65130 <= c && c <= 65131) return true;
            if (c == 65380) return true;
            if (c == 65509) return true;
        }
        return false;
    }

    //统计汉字个数的方法
    public static int countZh(String text) {
        int amount = 0;
        for (int i = 0; i < text.length(); i++) {
            boolean matches = isNormalChineseChar(text.charAt(i));
            if (matches) {
                amount++;
            }
        }
        return amount;
    }

    //移除中文空格
    public static String removeZhBlank(String text) {
        text = text.replaceAll(" +", " ");
        StringBuilder deleteString = new StringBuilder();
        deleteString.append(text.charAt(0));
        for (int i = 1; i < text.length() - 1; i++) {
            //空格2边都是中文字符
            if (text.charAt(i) == ' ' && isNormalChineseChar(text.charAt(i - 1)) && isNormalChineseChar(text.charAt(i + 1))) {
                continue;
            }
            deleteString.append(text.charAt(i));
        }
        deleteString.append(text.charAt(text.length() - 1));
        return deleteString.toString();
    }

    public static boolean isSentenceSeparator(char[] array, int index) {
        char c = array[index];
        for (char c1 : SENTENCE_SEPARATOR) {
            if (c == c1) {
                if (!(c == '.' && 0 < index && index < array.length - 1 && CharacterHelper.isNumberChar(array[index - 1]) && CharacterHelper.isNumberChar(array[index + 1]))) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 自定义的段落切分算法
     *
     * @param str        段落内容
     * @param partLength 切分的长度
     * @param part       切分的份数
     * @return 子串列表
     */
    public static String[] myCut(String str, int partLength, int part) {
        if (null == str) {
            return null;
        } else {
            int len = str.length();
            if (len < partLength) {
                return new String[]{str};
            } else {
                String[] array = new String[part];
                int start = 0;
                for (int i = 0; i < part; ++i) {
                    int oldIndex = i == part - 1 ? len - 1 : partLength + i * partLength;
                    int newIndex = getNewIndex(str, oldIndex);
                    array[i] = str.substring(start, newIndex).trim();
                    start = newIndex;
                }
                return array;
            }
        }
    }

    private static int getNewIndex(String content, int index) {
        char[] chars = content.toCharArray();
        int left = 0, right = 0;
        int count = 0;
        int newIndex = 0;
        //从左往右查找最多查找50个字
        for (int i = index; i < content.length(); i++) {
            count++;
            if (count == 100) {
                break;
            }
            if (CharacterHelper.isSentenceSeparator(chars, i)) {
                left = i;
                break;
            }
        }

        count = 0;
        //从右往左查找最多查找50个字
        for (int i = index; i >= 0; i--) {
            count++;
            if (count == 100) {
                break;
            }
            if (CharacterHelper.isSentenceSeparator(chars, i)) {
                right = i;
                break;
            }
        }

        //两边都没有找到的情况直接返回
        if (left == 0 && right == 0) {
            return index;
        }

        if (Math.abs(left - index) < Math.abs(right - index)) {
            newIndex = left;
        } else {
            newIndex = right;
        }
        //加1是为了把标点符号带上
        return newIndex + 1;
    }
}
